Getting set up

Start an RStudio session at OSC

Show instructions

  • Fill out the form as shown here.

  • Now, you should see a box like this:

  • Your job should start running pretty soon, and when it’s ready the box should look like this:

  • Click Connect to RStudio Server at the bottom of the box, and an RStudio Server instance will open. You’re ready to go!

Create an RStudio Project

Show instructions
basedir <- "/fs/project/PAS0471/teach/misc/2021-02_rnaseq/"

# Get your user name (by running a shell command via the `system()` function:
me <- system("echo $USER", intern = TRUE)

# Build the path to the target dir:
proj_dir <- file.path(basedir, me)

# Create the Project:
library(usethis)
create_project(path = proj_dir)

Now, RStudio will reload with the newly created Project open.

If you get the pop-up below, click Don't Save (do this whenever you get that pop-up):

Copy the count data and the metadata files

Go the Terminal tab next to the R Console in the bottom left of the RStudio window:

cd /fs/project/PAS0471/teach/misc/2021-02_rnaseq/$USER

cp ../master/data/meta/metadata.txt data/meta
cp ../master/results/count/gene_counts_all.txt results/count

Load the necessary packages

if(! "pacman" %in% installed.packages()) install.packages("pacman")

packages <- c("DESeq2",          # Differential expression analysis
              "tidyverse",       # Misc. data manipulation and plotting
              "here",            # Managing file paths
              "pheatmap",        # Heatmap plot
              "apeglm")          # For LFC shrinkage
pacman::p_load(char = packages)

theme_set(theme_bw())  # Set ggplot theme

Input and output dirs and files

Input files:

  • Gene counts table – the file exactly as it was written by featureCounts.

  • Metadata table – so we can group our samples and make comparisons between these groups.

count_table_file <- here("results/count/gene_counts_all.txt")
metadata_file <- here("data/meta/metadata.txt")

Output directories – and we create them if they don’t already exist:

outdir <- here("results/DE/")
plotdir <- here("results/DE/fig/")

if (!dir.exists(plotdir)) dir.create(plotdir, recursive = TRUE)
if (!dir.exists(plotdir)) dir.create(plotdir, recursive = TRUE)

Load input data

Load the count table from featureCounts:

raw_counts <- read.table(count_table_file,
                         sep = "\t", header = TRUE, skip = 1)

Load the metadata information:

metadata <- read.table(metadata_file, header = TRUE)

head(metadata)
##   SampleID Experiment GH_trial AMF           Treatment
## 1  C6_myb2       CAPS   CAPS_6  Ri Agrobacterium_noexp
## 2  C6_myb3       CAPS   CAPS_6  Ri Agrobacterium_noexp
## 3  C6_myb4       CAPS   CAPS_6  Ri Agrobacterium_noexp
## 4    CT_05       CAPS   CAPS_6  Ri                mock
## 5    CT_06       CAPS   CAPS_6  Ri                mock
## 6    CT_09       CAPS   CAPS_6  Ri                mock

The Treatment column currently has the values Agrobacterium_noexp, Agrobacterium_myb, and mock.

To shorten this a bit, we’ll get rid of “Agrobacterium_”:

metadata$Treatment <- sub("Agrobacterium_", "", metadata$Treatment)

unique(metadata$Treatment)
## [1] "noexp" "mock"  "myb"



Prepare the data

Change the column names, which are very long:

colnames(raw_counts)[7:8]
## [1] "X.fs.scratch.PAS1548.JamesSeq.CAPS.tomato.star_out.X6465_Benitez.PonceM_C6_myb2_V1N_1_S1_L001_R1_001.fastq.gzAligned.sortedByCoord.out.bam"
## [2] "X.fs.scratch.PAS1548.JamesSeq.CAPS.tomato.star_out.X6465_Benitez.PonceM_C6_myb2_V1N_1_S1_L002_R1_001.fastq.gzAligned.sortedByCoord.out.bam"
my_regex <- ".+PonceM_(.+)_V1N.+"
colnames(raw_counts) <- sub(my_regex, "\\1", colnames(raw_counts))

colnames(raw_counts)
##  [1] "Geneid"  "Chr"     "Start"   "End"     "Strand"  "Length"  "C6_myb2"
##  [8] "C6_myb2" "C6_myb3" "C6_myb3" "C6_myb4" "C6_myb4" "CT_05"   "CT_05"  
## [15] "CT_06"   "CT_06"   "CT_09"   "CT_09"   "T_26"    "T_26"    "T_29"   
## [22] "T_29"    "T_30"    "T_30"    "cnt_1"   "cnt_1"   "cnt_2"   "cnt_2"  
## [29] "cnt_3"   "cnt_3"   "myb_1"   "myb_1"   "myb_2"   "myb_2"   "myb_3"  
## [36] "myb_3"

Besides the counts, there are columns with metadata for each gene:

raw_counts[1:5, 1:8]
##             Geneid       Chr  Start    End Strand Length C6_myb2 C6_myb2.1
## 1 Solyc00g160260.1 SL4.0ch00  83863  84177      +    315       0         0
## 2 Solyc00g160270.1 SL4.0ch00 166754 167268      -    515       0         0
## 3 Solyc00g500003.1 SL4.0ch00 311496 382066      -  70571       1         4
## 4 Solyc00g500004.1 SL4.0ch00 417592 418482      +    891       0         0
## 5 Solyc00g500005.1 SL4.0ch00 478389 478640      +    252       0         0

Let’s remove those:

counts <- raw_counts[, 7:ncol(raw_counts)]
rownames(counts) <- raw_counts$Geneid

Replicate samples

In this table, there are two separate entries for each sample: each library was sequenced on two lanes. Recall that in our workflow, we had merged these FASTQ files prior to mapping, but here we are using the table based on the full dataset produced by Matthew.

So, we will go ahead and merge these replicates now, by simply summing the counts:

counts.t <- t(counts)
rownames(counts.t) <- names(raw_counts)[7:36]
sums <- rowsum(counts.t, group = rownames(counts.t))
counts <- t(sums)

(Alternatively, one could use the specialized function DESeq2::collapseReplicates() for this.)

Check sample IDs

For differential expression analysis, we will be using the popularDESeq2 R/Bioconductor package (paper, website).

We will load both the count table and the metadata into DESeq2. When doing so, DESeq2 assumes that sample IDs in both tables match and are provided in the same order. Let’s make sure this is indeed the case.

Sort both data frames alphabetically:

metadata <- metadata[order(metadata$SampleID), ]
counts <- counts[, order(colnames(counts))]

Check if names are the same:

metadata$SampleID
##  [1] "C6_myb2" "C6_myb3" "C6_myb4" "cnt_1"   "cnt_2"   "cnt_3"   "CT_05"  
##  [8] "CT_06"   "CT_09"   "myb_1"   "myb_2"   "myb_3"   "T_26"    "T_29"   
## [15] "T_30"
colnames(counts)
##  [1] "C6_myb2" "C6_myb3" "C6_myb4" "cnt_1"   "cnt_2"   "cnt_3"   "CT_05"  
##  [8] "CT_06"   "CT_09"   "myb_1"   "myb_2"   "myb_3"   "T_26"    "T_29"   
## [15] "T_30"
matching_names <- identical(metadata$SampleID, colnames(counts))
matching_names
## [1] TRUE
if(matching_names == FALSE) stop("Sample ID in metadata and count matrix do not match!")

Create the DESeq2 object

We will create the DESeq2 object using the function DESeqDataSetFromMatrix(), which we will provide with three pieces of information:

  • The count data with argument countData.
  • The metadata with argument colData.
  • The model design for the DE analysis – argument design.
    For now, we will specify ~1, which means “no design” – we will change this before the actual DE analysis.
dds_raw <- DESeqDataSetFromMatrix(countData = counts,
                                  colData = metadata,
                                  design = ~ 1)



Explore the count data

What are number of rows and columns of the count matrix?

dim(counts)
## [1] 34688    15

How many genes have non-zero counts?

dim(counts[rowSums(counts) > 0, ])
## [1] 28145    15

How many genes have total counts of at least 10?

dim(counts[rowSums(counts) >= 10, ])
## [1] 24771    15

Histogram of gene counts

Let’s plot a histogram of gene counts:

theme_set(theme_bw())

summed_gene_counts <- data.frame(gene_count = rowSums(counts)) %>%
  rownames_to_column("gene_id")

ggplot(data = summed_gene_counts) +
  geom_histogram(aes(x = gene_count), binwidth = 10000) +
  scale_y_log10(expand = c(0, 0)) +
  scale_x_continuous(expand = c(0,0))

Zoom in a bit:

ggplot(data = summed_gene_counts) +
  geom_histogram(aes(x = gene_count), binwidth = 1000) +
  scale_y_log10(expand = c(0, 0)) +
  scale_x_continuous(limits = c(0, 200000), expand = c(0,0)) +
  theme(plot.margin = margin(0.5, 0.7, 0.5, 0.5, "cm"))

How are counts distributed across samples? That is, we would like a sum of counts for each column. To get this, we use the apply() function, which can apply a function (in our case sum()) to all columns (hence MARGIN = 2 – for rows, use 1) of our counts dataframe:

apply(X = counts, MARGIN = 2, FUN = sum)
##  C6_myb2  C6_myb3  C6_myb4    cnt_1    cnt_2    cnt_3    CT_05    CT_06 
## 18122406 21913320 13659702 18844494 18890045 16634169 22183005 20620412 
##    CT_09    myb_1    myb_2    myb_3     T_26     T_29     T_30 
## 22924853 22070790 22829207 23195471 19902716 23221528 15793928



Principal Component Analysis (PCA)

Run the PCA and prepare for plotting

First, we normalize the count data to have even sampling across samples (with respect to library size) and approximately even variance:

vsd <- varianceStabilizingTransformation(dds_raw, blind = TRUE)

Next, we run the PCA and retrieve the data to plot with ggplot2:

pcaData <- plotPCA(vsd,
                   ntop = 500,
                   intgroup = c("AMF", "Treatment"),
                   returnData = TRUE)

We extract the percentage of variance explained by different principal components, so we can later add this information to the plot:

percentVar <- round(100 * attr(pcaData, "percentVar"))
percentVar
## [1] 59 15

We create a plot title with the species name in italic using the somewhat bizarre expression() function:

plot_title <- expression("PCA of " * italic(Glycine ~ max) * " transcript count data")

Plot the PCA results

ggplot(pcaData,
       aes(x = PC1, y = PC2, color = AMF, shape = Treatment)) +
  geom_point(size = 6) +
  xlab(paste0("PC1: ", percentVar[1], "% of variance")) +
  ylab(paste0("PC2: ", percentVar[2], "% of variance")) +
  ggtitle(plot_title)

Plot again – with sample names

library(ggrepel)

pca_plot2 <- ggplot(pcaData,
              aes(PC1, PC2, color = AMF, shape = Treatment)) +
  geom_point(size = 3) +
  geom_label_repel(aes(label = name)) +
  xlab(paste0("PC1: ", percentVar[1], "% of variance")) +
  ylab(paste0("PC2: ", percentVar[2], "% of variance")) +
  ggtitle(plot_title)

print(pca_plot2)



DE analysis – full dataset

The design has two factors: AMF and Treatment. Rather than fit a multivariate model, we can start by merging the two into a single factor called group, and fit a univariate model with this factor.

dds_raw$group <- factor(paste(dds_raw$AMF, dds_raw$Treatment, sep = "_"))
table(dds_raw$group)
## 
## control_mock  control_myb      Ri_mock       Ri_myb     Ri_noexp 
##            3            3            3            3            3

We will set the “reference” level of the factor to be the double negative control (empty substrate, no Agrobacteria):

dds_raw$group <- relevel(dds_raw$group, ref = "control_mock")
dds_raw$group
##  [1] Ri_noexp     Ri_noexp     Ri_noexp     control_mock control_mock
##  [6] control_mock Ri_mock      Ri_mock      Ri_mock      control_myb 
## [11] control_myb  control_myb  Ri_myb       Ri_myb       Ri_myb      
## Levels: control_mock control_myb Ri_mock Ri_myb Ri_noexp

Next, we set the analysis design:

design(dds_raw) <- formula(~ group)

And finally, we perform the differential expression analysis with the DEseq() function:

dds <- DESeq(dds_raw)

The DESeq() function above performs three steps consecutively:

  • estimateSizeFactors() – “Normalization” by library size. Note that DESeq2 doesn’t actually normalize the counts, it uses raw counts and includes the size factors in the modeling.

  • estimateDispersions() – Estimate gene-wise dispersions.

  • nbinomWaldTest(ddsObj) – Fit the negative Binomial GLM and calculate Wald statistics.

These functions could also be called separately, which would be useful if you want to be able to change more defaults.

The results table

res <- results(dds)
res
## log2 fold change (MLE): group Ri noexp vs control mock 
## Wald test p-value: group Ri noexp vs control mock 
## DataFrame with 34688 rows and 6 columns
##                   baseMean log2FoldChange     lfcSE      stat    pvalue
##                  <numeric>      <numeric> <numeric> <numeric> <numeric>
## Solyc00g160260.1   0.00000             NA        NA        NA        NA
## Solyc00g160270.1   0.00000             NA        NA        NA        NA
## Solyc00g500003.1   9.39133       -2.20718   1.24311  -1.77554 0.0758095
## Solyc00g500004.1   0.00000             NA        NA        NA        NA
## Solyc00g500005.1   0.00000             NA        NA        NA        NA
## ...                    ...            ...       ...       ...       ...
## Solyc12g100330.2   1140.38       0.701332  0.274283  2.556962 0.0105591
## Solyc12g100340.1      0.00             NA        NA        NA        NA
## Solyc12g100360.1   1552.93      -0.285219  0.306501 -0.930565 0.3520788
## Solyc12g100363.1      0.00             NA        NA        NA        NA
## Solyc12g100367.1      0.00             NA        NA        NA        NA
##                       padj
##                  <numeric>
## Solyc00g160260.1        NA
## Solyc00g160270.1        NA
## Solyc00g500003.1  0.241664
## Solyc00g500004.1        NA
## Solyc00g500005.1        NA
## ...                    ...
## Solyc12g100330.2  0.067777
## Solyc12g100340.1        NA
## Solyc12g100360.1  0.593960
## Solyc12g100363.1        NA
## Solyc12g100367.1        NA

By default, the results table prints statistics comparing the last level of the factor with the first level: that is, log-fold change and p-values describe differences between these two levels specifically. However, we can easily extract equivalent statistics for any pairwise comparison among our factor levels, which we will see later.

For now, we will explore what each column in this table means:

  • The baseMean column contains the mean expression level across all samples.

  • The log2FoldChange column contains the log2-fold change of gene counts between the compared levels, that is, it represents the effect size. A log2-fold change of 1 indicates that the expression in the reference level is 2 times lower than that of the other level, a log2-fold change of 2 indicates a four-fold difference, a log2-fold change of 3 indicates an eight-fold difference, and so on. Similarly, negative log2-fold change values indicate an change in gene counts in the other direction.

  • The lfcSE column indicates the uncertainty in terms of the standard error (SE) of the log2-fold change estimate.

  • The stat column indicates the value for the Wald test’s test statistic.

  • The pvalue column reported the uncorrected p-value from the Wald test.

  • Because we are testing significance for many genes, we need to correct for multiple testing. DESeq2 uses the Benjamini-Hochberg False Discovery Rate (FDR) correction, and these values are reported in the column padj (i.e., adjusted p-value).

A summary of this information about each column can be seen by running the mcols() function:

mcols(res)
## DataFrame with 6 rows and 2 columns
##                        type            description
##                 <character>            <character>
## baseMean       intermediate mean of normalized c..
## log2FoldChange      results log2 fold change (ML..
## lfcSE               results standard error: grou..
## stat                results Wald statistic: grou..
## pvalue              results Wald test p-value: g..
## padj                results   BH adjusted p-values

NA values in the results table

Some values in the results table can be set to NA for one of the following reasons:

  • If within a row, all samples have zero counts, the baseMean column will be zero, and the log2-fold change estimates, p-value and adjusted p-value will all be set to NA.

  • DESeq2 performs outlier detection using Cook’s distance. If a gene contains a sample with an count outlier, both the p-value and adjusted p-value will be set to NA.

  • DESeq2 also automatically filters low mean count genes in the sense that it does not include them in the multiple testing correction. Therefore, in such cases, the p-value will not be NA, but the adjusted p-value will be.

    Because we have very low power to detect differential expression for such low-count genes, it is beneficial to remove them prior to the multiple testing correction, so this correction becomes less severe for the remaining genes.

Let’s see how many genes have NA p-values:

# Number of genes with NA p-value:
sum(is.na(res$pvalue))
## [1] 6601
# As a proportion of the total number of genes in the test:
sum(is.na(res$pvalue)) / nrow(res)
## [1] 0.1902964

And NA adjusted p-values:

# Number of genes with NA p-value:
sum(is.na(res$padj))
## [1] 11991
# As a proportion of the total number of genes in the test:
sum(is.na(res$padj)) / nrow(res)
## [1] 0.3456815



DE analysis – contrast two custom groups

Although not displayed in a particularly readable ways, we can see which pairwise contrasts between different levels of the factor are available:

resultsNames(dds)
## [1] "Intercept"                         "group_control_myb_vs_control_mock"
## [3] "group_Ri_mock_vs_control_mock"     "group_Ri_myb_vs_control_mock"     
## [5] "group_Ri_noexp_vs_control_mock"

Not all pairwise contrasts between the 5 levels in our group factor are available: instead, control_mock, which we set as the reference level, is being compared with all 4 other levels.

Above, we looked at the results (p-values and so on) for the last of these comparisons, which is what DESeq2 will show by default. To see the results table for one of the other 3 comparisons, we pass a vector to the contrast argument of the results() function with the factor (group) and the two levels to be contrasted:

my_contrast <- c("Ri_mock", "control_mock")

res <- results(dds,
               contrast = c("group", my_contrast))

How many adjusted p-values were less than 0.1?

sum(res$padj < 0.1, na.rm = TRUE)
## [1] 9014

We’ll also create an object with adjusted (shrunken) LFC estimates which will be useful for visualization and ranking of genes by LFC.

my_coef <- paste0("group_", paste0(my_contrast, collapse = "_vs_"))
my_coef
## [1] "group_Ri_mock_vs_control_mock"
res_LFC <- lfcShrink(dds, coef = my_coef, type = "apeglm", lfcThreshold = 1)

Note that here, we had to provide the contrast (“coefficient”) in the format given by resultsNames(dds), which looks confusing because it uses underscores as a separator, and our factor levels also contain underscores.



Exploring the results

MA-plot:

For a nice overview of the results, we can plot a so-called “MA plot”, which plots, for each gene, count differences (as LFC) between two groups on the y-axis, and mean counts across both groups on the x-axis.

We can create an MA plot using DESeq2’s plotMA function, with significantly differentially expressed genes (using alpha = 0.1) in blue:

plotMA(res, ylim = c(-5, 5))

To be able to customize the plot, we’ll use returnData = TRUE like we have done with previous plots, and then plot the resulting dataframe with ggplot2:

d <- plotMA(res, returnData = TRUE)

ggplot(d, aes(x = mean, y = lfc, color = isDE)) +
  geom_point(size = 0.5) +
  scale_x_log10() +
  scale_y_continuous(limits = c(-10, 10)) +
  scale_color_manual(values = c('grey50', 'blue')) +
  guides(color = FALSE) +
  labs(x = "Mean of normalized counts",
       y = "LFC")

We can see that lowly-expressed genes tend to deviate from an LFC of 0 (same mean expression levels in the two groups) much more than 0. However, this is an artifact of noise overpowering signal with such low expression values. We can also see that genes are never significantly expressed in the same far left part of the plot due to this lack of power.

DESeq2 provides several methods to adjust LFC estimates for the low-expression bias, one of which we used above to produce the res_LFC object. Let’s create another MA plot for these adjusted estimates:

d <- plotMA(res_LFC, ylim = c(-5, 5), returnData = TRUE)

ggplot(d, aes(x = mean, y = lfc, color = isDE)) +
  geom_point(size = 0.5) +
  scale_x_log10() +
  scale_color_manual(values = c('grey50', 'blue')) +
  guides(color = FALSE) +
  labs(x = "Mean of normalized counts",
       y = "Shrunken LFC")

We can also make this plot interactive with Plotly, so we can see the identity of each gene by hovering over the point:

library(plotly)

d$gene <- rownames(d)

p_ma <- ggplot(d,
               aes(x = mean, y = lfc, color = isDE, text = gene)) +
  geom_point(size = 0.5) +
  scale_x_log10() +
  scale_color_manual(values = c('grey50', 'blue')) +
  guides(color = FALSE) +
  labs(x = "Mean of normalized counts",
       y = "Shrunken LFC")

ggplotly(p_ma, tooltip = "text")

Plot specific genes

We can also create plot of expression levels in individual genes, which will be especially interesting for genes with highly significant differential expression.

Let’s plot the top-5 most significantly differentially expressed genes:

top5 <- row.names(res[order(res$padj), ][1:5, ])

for(fgene in top5) {
  
  d <- plotCounts(dds,
                  gene = fgene,
                  intgroup = "group",
                  returnData = TRUE)

  p <- ggplot(d, aes(x = group, y = count)) +
          geom_point(position = position_jitter(w = 0.1, h = 0)) +
          labs(title = fgene) +
          theme_bw()
  
  print(p)
}

Heatmap

We can create heatmaps with the pheatmap function – for instance, for the 20 most highly expressed genes:

ntd <- normTransform(dds)
selection <- order(rowMeans(counts(dds, normalized = TRUE)),
                   decreasing = TRUE)[1:20]
ntd_sel <- assay(ntd)[selection, ]
df_meta <- as.data.frame(colData(dds)[, c("AMF", "Treatment")])
pheatmap(ntd_sel,
         cluster_rows = FALSE,
         cluster_cols = FALSE,
         show_rownames = FALSE,
         annotation_col = df_meta)

Export the results

TBA



DE analysis – with two factors

TBA

LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBhbmFseXNpcyA8YnI+IHdpdGggREVTZXEyIgphdXRob3I6ICJKZWxtZXIgUG9lbHN0cmEiCmRhdGU6ICIzLzI2LzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBhbmNob3Jfc2VjdGlvbnM6IHRydWUKICAgIGNzczogaHRtbF9wYWdlLmNzcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobz1UUlVFLCBldmFsPVRSVUUsIGNhY2hlPVRSVUUsCiAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwKICBjbGFzcy5zb3VyY2UgPSAicl9jb2RlIiwKICBjbGFzcy5vdXRwdXQgPSAicl9vdXRwdXQiLAogIGNsYXNzLndhcm5pbmcgPSAicl93YXJuaW5nIiwKICBjbGFzcy5tZXNzYWdlID0gInJfd2FybmluZyIsCiAgY2xhc3MuZXJyb3IgPSAicl9lcnJvciIKICApCmBgYAoKPGJyPiA8YnI+CgotLS0tCgojIyBHZXR0aW5nIHNldCB1cAoKIyMjIFN0YXJ0IGFuIFJTdHVkaW8gc2Vzc2lvbiBhdCBPU0MKCjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgaW5zdHJ1Y3Rpb25zPC9zdW1tYXJ5PgoKLSBMb2dpbiB0byBPU0MgYXQgPGh0dHBzOi8vb25kZW1hbmQub3NjLmVkdT4uCgotIENsaWNrIG9uIGBJbnRlcmFjdGl2ZSBBcHBzYCAodG9wIGJhcikgPiBgUlN0dWRpbyBTZXJ2ZXIgKE93ZW5zIGFuZCBQaXR6ZXIpYAoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9hcHBzLnBuZyB3aWR0aD0iNTAwIj4KPC9wPgoKLSBGaWxsIG91dCB0aGUgZm9ybSBhcyBzaG93biBbaGVyZV0oc2xpZGVzLzAzLU9TQy1zbGlkZXMuaHRtbCNyc3R1ZGlvX3NlcnZlcl9qb2IpLgoKLSBOb3csIHlvdSBzaG91bGQgc2VlIGEgYm94IGxpa2UgdGhpczoKCjxwIGFsaWduPSJjZW50ZXIiPgo8aW1nIHNyYz1pbWcvb3NjX3F1ZXVlZC5wbmcgd2lkdGg9IjcwMCI+CjwvcD4KCi0gWW91ciBqb2Igc2hvdWxkIHN0YXJ0IHJ1bm5pbmcgcHJldHR5IHNvb24sIGFuZCB3aGVuIGl0J3MgcmVhZHkgdGhlIGJveCBzaG91bGQgbG9vayBsaWtlIHRoaXM6IAoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9vc2NfcnVubmluZy5wbmcgd2lkdGg9IjcwMCI+CjwvcD4KCi0gQ2xpY2sgYENvbm5lY3QgdG8gUlN0dWRpbyBTZXJ2ZXJgIGF0IHRoZSBib3R0b20gb2YgdGhlIGJveCwgYW5kIGFuIFJTdHVkaW8gU2VydmVyIGluc3RhbmNlIHdpbGwgb3Blbi4gWW91J3JlIHJlYWR5IHRvIGdvIQoKPC9kZXRhaWxzPgoKIyMjIENyZWF0ZSBhbiBSU3R1ZGlvIFByb2plY3QKCjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgaW5zdHJ1Y3Rpb25zPC9zdW1tYXJ5PgoKYGBge3IsIGV2YWw9RkFMU0V9CmJhc2VkaXIgPC0gIi9mcy9wcm9qZWN0L1BBUzA0NzEvdGVhY2gvbWlzYy8yMDIxLTAyX3JuYXNlcS8iCgojIEdldCB5b3VyIHVzZXIgbmFtZSAoYnkgcnVubmluZyBhIHNoZWxsIGNvbW1hbmQgdmlhIHRoZSBgc3lzdGVtKClgIGZ1bmN0aW9uOgptZSA8LSBzeXN0ZW0oImVjaG8gJFVTRVIiLCBpbnRlcm4gPSBUUlVFKQoKIyBCdWlsZCB0aGUgcGF0aCB0byB0aGUgdGFyZ2V0IGRpcjoKcHJval9kaXIgPC0gZmlsZS5wYXRoKGJhc2VkaXIsIG1lKQoKIyBDcmVhdGUgdGhlIFByb2plY3Q6CmxpYnJhcnkodXNldGhpcykKY3JlYXRlX3Byb2plY3QocGF0aCA9IHByb2pfZGlyKQpgYGAKCk5vdywgUlN0dWRpbyB3aWxsIHJlbG9hZCB3aXRoIHRoZSBuZXdseSBjcmVhdGVkIFByb2plY3Qgb3Blbi4KCklmIHlvdSBnZXQgdGhlIHBvcC11cCBiZWxvdywgY2xpY2sgYERvbid0IFNhdmVgIChkbyB0aGlzIHdoZW5ldmVyIHlvdSBnZXQgdGhhdCBwb3AtdXApOgoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9yZGF0YS1wb3B1cC5wbmcgd2lkdGg9IjM1MCI+CjwvcD4KCjwvZGV0YWlscz4KCiMjIyBDb3B5IHRoZSBjb3VudCBkYXRhIGFuZCB0aGUgbWV0YWRhdGEgZmlsZXMKCkdvIHRoZSBgVGVybWluYWxgIHRhYiBuZXh0IHRvIHRoZSBSIGBDb25zb2xlYCBpbiB0aGUgYm90dG9tIGxlZnQgb2YgdGhlIFJTdHVkaW8Kd2luZG93OgoKYGBge3NoLCBldmFsPUZBTFNFfQpjZCAvZnMvcHJvamVjdC9QQVMwNDcxL3RlYWNoL21pc2MvMjAyMS0wMl9ybmFzZXEvJFVTRVIKCmNwIC4uL21hc3Rlci9kYXRhL21ldGEvbWV0YWRhdGEudHh0IGRhdGEvbWV0YQpjcCAuLi9tYXN0ZXIvcmVzdWx0cy9jb3VudC9nZW5lX2NvdW50c19hbGwudHh0IHJlc3VsdHMvY291bnQKYGBgCgojIyMgTG9hZCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KaWYoISAicGFjbWFuIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQoKcGFja2FnZXMgPC0gYygiREVTZXEyIiwgICAgICAgICAgIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwogICAgICAgICAgICAgICJ0aWR5dmVyc2UiLCAgICAgICAjIE1pc2MuIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBwbG90dGluZwogICAgICAgICAgICAgICJoZXJlIiwgICAgICAgICAgICAjIE1hbmFnaW5nIGZpbGUgcGF0aHMKICAgICAgICAgICAgICAicGhlYXRtYXAiLCAgICAgICAgIyBIZWF0bWFwIHBsb3QKICAgICAgICAgICAgICAiYXBlZ2xtIikgICAgICAgICAgIyBGb3IgTEZDIHNocmlua2FnZQpwYWNtYW46OnBfbG9hZChjaGFyID0gcGFja2FnZXMpCgp0aGVtZV9zZXQodGhlbWVfYncoKSkgICMgU2V0IGdncGxvdCB0aGVtZQpgYGAKCiMjIyBJbnB1dCBhbmQgb3V0cHV0IGRpcnMgYW5kIGZpbGVzCgpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpjb3VudF90YWJsZV9maWxlIDwtICJyZXN1bHRzL2NvdW50L2dlbmVfY291bnRzX2FsbC50eHQiCm1ldGFkYXRhX2ZpbGUgPC0gImRhdGEvbWV0YS9tZXRhZGF0YS50eHQiCgpvdXRkaXIgPC0gInJlc3VsdHMvREUvIgpwbG90ZGlyIDwtICJyZXN1bHRzL0RFL2ZpZy8iCmBgYAoKSW5wdXQgZmlsZXM6CgotICoqR2VuZSBjb3VudHMgdGFibGUqKiAtLSB0aGUgZmlsZSBleGFjdGx5IGFzIGl0IHdhcyB3cml0dGVuIGJ5ICpmZWF0dXJlQ291bnRzKi4KICAKLSAqKk1ldGFkYXRhIHRhYmxlKiogLS0gc28gd2UgY2FuIGdyb3VwIG91ciBzYW1wbGVzIGFuZCBtYWtlIGNvbXBhcmlzb25zIGJldHdlZW4KICB0aGVzZSBncm91cHMuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY291bnRfdGFibGVfZmlsZSA8LSBoZXJlKCJyZXN1bHRzL2NvdW50L2dlbmVfY291bnRzX2FsbC50eHQiKQptZXRhZGF0YV9maWxlIDwtIGhlcmUoImRhdGEvbWV0YS9tZXRhZGF0YS50eHQiKQpgYGAKCk91dHB1dCBkaXJlY3RvcmllcyAtLSBhbmQgd2UgY3JlYXRlIHRoZW0gaWYgdGhleSBkb24ndCBhbHJlYWR5IGV4aXN0OgoKYGBge3J9Cm91dGRpciA8LSBoZXJlKCJyZXN1bHRzL0RFLyIpCnBsb3RkaXIgPC0gaGVyZSgicmVzdWx0cy9ERS9maWcvIikKCmlmICghZGlyLmV4aXN0cyhwbG90ZGlyKSkgZGlyLmNyZWF0ZShwbG90ZGlyLCByZWN1cnNpdmUgPSBUUlVFKQppZiAoIWRpci5leGlzdHMocGxvdGRpcikpIGRpci5jcmVhdGUocGxvdGRpciwgcmVjdXJzaXZlID0gVFJVRSkKYGBgCgojIyMgTG9hZCBpbnB1dCBkYXRhCgpMb2FkIHRoZSBjb3VudCB0YWJsZSBmcm9tICpmZWF0dXJlQ291bnRzKjoKCmBgYHtyLCBldmFsPVRSVUV9CnJhd19jb3VudHMgPC0gcmVhZC50YWJsZShjb3VudF90YWJsZV9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwgaGVhZGVyID0gVFJVRSwgc2tpcCA9IDEpCmBgYAoKTG9hZCB0aGUgbWV0YWRhdGEgaW5mb3JtYXRpb246CgpgYGB7cn0KbWV0YWRhdGEgPC0gcmVhZC50YWJsZShtZXRhZGF0YV9maWxlLCBoZWFkZXIgPSBUUlVFKQoKaGVhZChtZXRhZGF0YSkKYGBgCgpUaGUgYFRyZWF0bWVudGAgY29sdW1uIGN1cnJlbnRseSBoYXMgdGhlIHZhbHVlcyBgQWdyb2JhY3Rlcml1bV9ub2V4cGAsCmBBZ3JvYmFjdGVyaXVtX215YmAsIGFuZCBgbW9ja2AuCgpUbyBzaG9ydGVuIHRoaXMgYSBiaXQsIHdlJ2xsIGdldCByaWQgb2YgIkFncm9iYWN0ZXJpdW1fIjoKCmBgYHtyfQptZXRhZGF0YSRUcmVhdG1lbnQgPC0gc3ViKCJBZ3JvYmFjdGVyaXVtXyIsICIiLCBtZXRhZGF0YSRUcmVhdG1lbnQpCgp1bmlxdWUobWV0YWRhdGEkVHJlYXRtZW50KQpgYGAKCgo8YnI+CgotLS0tCgojIyBQcmVwYXJlIHRoZSBkYXRhCgpDaGFuZ2UgdGhlIGNvbHVtbiBuYW1lcywgd2hpY2ggYXJlIHZlcnkgbG9uZzoKCmBgYHtyfQpjb2xuYW1lcyhyYXdfY291bnRzKVs3OjhdCmBgYAoKYGBge3J9Cm15X3JlZ2V4IDwtICIuK1BvbmNlTV8oLispX1YxTi4rIgpjb2xuYW1lcyhyYXdfY291bnRzKSA8LSBzdWIobXlfcmVnZXgsICJcXDEiLCBjb2xuYW1lcyhyYXdfY291bnRzKSkKCmNvbG5hbWVzKHJhd19jb3VudHMpCmBgYAoKQmVzaWRlcyB0aGUgY291bnRzLCB0aGVyZSBhcmUgY29sdW1ucyB3aXRoIG1ldGFkYXRhIGZvciBlYWNoIGdlbmU6CgpgYGB7cn0KcmF3X2NvdW50c1sxOjUsIDE6OF0KYGBgCgpMZXQncyByZW1vdmUgdGhvc2U6CgpgYGB7cn0KY291bnRzIDwtIHJhd19jb3VudHNbLCA3Om5jb2wocmF3X2NvdW50cyldCnJvd25hbWVzKGNvdW50cykgPC0gcmF3X2NvdW50cyRHZW5laWQKYGBgCgojIyMgUmVwbGljYXRlIHNhbXBsZXMKCkluIHRoaXMgdGFibGUsIHRoZXJlIGFyZSB0d28gc2VwYXJhdGUgZW50cmllcyBmb3IgZWFjaCBzYW1wbGU6CmVhY2ggbGlicmFyeSB3YXMgc2VxdWVuY2VkIG9uIHR3byBsYW5lcy4KUmVjYWxsIHRoYXQgaW4gb3VyIHdvcmtmbG93LCB3ZSBoYWQgbWVyZ2VkIHRoZXNlIEZBU1RRIGZpbGVzIHByaW9yIHRvIG1hcHBpbmcsCmJ1dCBoZXJlIHdlIGFyZSB1c2luZyB0aGUgdGFibGUgYmFzZWQgb24gdGhlIGZ1bGwgZGF0YXNldCBwcm9kdWNlZCBieSBNYXR0aGV3LgoKU28sIHdlIHdpbGwgZ28gYWhlYWQgYW5kIG1lcmdlIHRoZXNlIHJlcGxpY2F0ZXMgbm93LCBieSBzaW1wbHkgc3VtbWluZyB0aGUgY291bnRzOgoKYGBge3J9CmNvdW50cy50IDwtIHQoY291bnRzKQpyb3duYW1lcyhjb3VudHMudCkgPC0gbmFtZXMocmF3X2NvdW50cylbNzozNl0Kc3VtcyA8LSByb3dzdW0oY291bnRzLnQsIGdyb3VwID0gcm93bmFtZXMoY291bnRzLnQpKQpjb3VudHMgPC0gdChzdW1zKQpgYGAKCihBbHRlcm5hdGl2ZWx5LCBvbmUgY291bGQgdXNlIHRoZSBzcGVjaWFsaXplZCBmdW5jdGlvbgpgREVTZXEyOjpjb2xsYXBzZVJlcGxpY2F0ZXMoKWAgZm9yIHRoaXMuKQoKIyMjIENoZWNrIHNhbXBsZSBJRHMKCkZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgd2Ugd2lsbCBiZSB1c2luZyB0aGUgcG9wdWxhcioqREVTZXEyKioKUi9CaW9jb25kdWN0b3IgcGFja2FnZQooW3BhcGVyXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE0LTA1NTAtOCksClt3ZWJzaXRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpKS4KCldlIHdpbGwgbG9hZCBib3RoIHRoZSBjb3VudCB0YWJsZSBhbmQgdGhlIG1ldGFkYXRhIGludG8gKkRFU2VxMiouCldoZW4gZG9pbmcgc28sICpERVNlcTIqIGFzc3VtZXMgdGhhdCBzYW1wbGUgSURzIGluIGJvdGggdGFibGVzIG1hdGNoIGFuZCAKYXJlIHByb3ZpZGVkIGluIHRoZSBzYW1lIG9yZGVyLiBMZXQncyBtYWtlIHN1cmUgdGhpcyBpcyBpbmRlZWQgdGhlIGNhc2UuCgpTb3J0IGJvdGggZGF0YSBmcmFtZXMgYWxwaGFiZXRpY2FsbHk6CgpgYGB7cn0KbWV0YWRhdGEgPC0gbWV0YWRhdGFbb3JkZXIobWV0YWRhdGEkU2FtcGxlSUQpLCBdCmNvdW50cyA8LSBjb3VudHNbLCBvcmRlcihjb2xuYW1lcyhjb3VudHMpKV0KYGBgCgpDaGVjayBpZiBuYW1lcyBhcmUgdGhlIHNhbWU6CgpgYGB7cn0KbWV0YWRhdGEkU2FtcGxlSUQKCmNvbG5hbWVzKGNvdW50cykKCm1hdGNoaW5nX25hbWVzIDwtIGlkZW50aWNhbChtZXRhZGF0YSRTYW1wbGVJRCwgY29sbmFtZXMoY291bnRzKSkKbWF0Y2hpbmdfbmFtZXMKaWYobWF0Y2hpbmdfbmFtZXMgPT0gRkFMU0UpIHN0b3AoIlNhbXBsZSBJRCBpbiBtZXRhZGF0YSBhbmQgY291bnQgbWF0cml4IGRvIG5vdCBtYXRjaCEiKQpgYGAKCiMjIyBDcmVhdGUgdGhlIERFU2VxMiBvYmplY3QKCldlIHdpbGwgY3JlYXRlIHRoZSBERVNlcTIgb2JqZWN0IHVzaW5nIHRoZSBmdW5jdGlvbiBgREVTZXFEYXRhU2V0RnJvbU1hdHJpeCgpYCwKd2hpY2ggd2Ugd2lsbCBwcm92aWRlIHdpdGggdGhyZWUgcGllY2VzIG9mIGluZm9ybWF0aW9uOgoKLSBUaGUgY291bnQgZGF0YSB3aXRoIGFyZ3VtZW50IGBjb3VudERhdGFgLgotIFRoZSBtZXRhZGF0YSB3aXRoIGFyZ3VtZW50IGBjb2xEYXRhYC4KLSBUaGUgbW9kZWwgZGVzaWduIGZvciB0aGUgREUgYW5hbHlzaXMgLS0gYXJndW1lbnQgYGRlc2lnbmAuICAKICBGb3Igbm93LCB3ZSB3aWxsIHNwZWNpZnkgYH4xYCwgd2hpY2ggbWVhbnMgIm5vIGRlc2lnbiIgLS0KICB3ZSB3aWxsIGNoYW5nZSB0aGlzIGJlZm9yZSB0aGUgYWN0dWFsIERFIGFuYWx5c2lzLgoKYGBge3J9CmRkc19yYXcgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IDEpCmBgYAoKPGJyPgoKLS0tLQoKIyMgRXhwbG9yZSB0aGUgY291bnQgZGF0YQoKV2hhdCBhcmUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMgb2YgdGhlIGNvdW50IG1hdHJpeD8KCmBgYHtyfQpkaW0oY291bnRzKQpgYGAKCkhvdyBtYW55IGdlbmVzIGhhdmUgbm9uLXplcm8gY291bnRzPwoKYGBge3J9CmRpbShjb3VudHNbcm93U3Vtcyhjb3VudHMpID4gMCwgXSkKCmBgYAoKSG93IG1hbnkgZ2VuZXMgaGF2ZSB0b3RhbCBjb3VudHMgb2YgYXQgbGVhc3QgMTA/CgpgYGB7cn0KZGltKGNvdW50c1tyb3dTdW1zKGNvdW50cykgPj0gMTAsIF0pCmBgYAoKIyMjIEhpc3RvZ3JhbSBvZiBnZW5lIGNvdW50cwoKTGV0J3MgcGxvdCBhIGhpc3RvZ3JhbSBvZiBnZW5lIGNvdW50czoKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCnN1bW1lZF9nZW5lX2NvdW50cyA8LSBkYXRhLmZyYW1lKGdlbmVfY291bnQgPSByb3dTdW1zKGNvdW50cykpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9pZCIpCgpnZ3Bsb3QoZGF0YSA9IHN1bW1lZF9nZW5lX2NvdW50cykgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZ2VuZV9jb3VudCksIGJpbndpZHRoID0gMTAwMDApICsKICBzY2FsZV95X2xvZzEwKGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKQpgYGAKClpvb20gaW4gYSBiaXQ6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzdW1tZWRfZ2VuZV9jb3VudHMpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGdlbmVfY291bnQpLCBiaW53aWR0aCA9IDEwMDApICsKICBzY2FsZV95X2xvZzEwKGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyMDAwMDApLCBleHBhbmQgPSBjKDAsMCkpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigwLjUsIDAuNywgMC41LCAwLjUsICJjbSIpKQpgYGAKCkhvdyBhcmUgY291bnRzIGRpc3RyaWJ1dGVkIGFjcm9zcyBzYW1wbGVzPwpUaGF0IGlzLCB3ZSB3b3VsZCBsaWtlIGEgc3VtIG9mIGNvdW50cyBmb3IgZWFjaCBjb2x1bW4uClRvIGdldCB0aGlzLCB3ZSB1c2UgdGhlIGBhcHBseSgpYCBmdW5jdGlvbiwgd2hpY2ggY2FuIGFwcGx5IGEgZnVuY3Rpb24KKGluIG91ciBjYXNlIGBzdW0oKWApIHRvIGFsbCBjb2x1bW5zIChoZW5jZSBgTUFSR0lOID0gMmAgLS0gZm9yIHJvd3MsIHVzZSBgMWApCm9mIG91ciBgY291bnRzYCBkYXRhZnJhbWU6CgpgYGB7cn0KYXBwbHkoWCA9IGNvdW50cywgTUFSR0lOID0gMiwgRlVOID0gc3VtKQpgYGAKCjxicj4KCi0tLS0KCiMjIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgCgojIyMgUnVuIHRoZSBQQ0EgYW5kIHByZXBhcmUgZm9yIHBsb3R0aW5nCgpGaXJzdCwgd2Ugbm9ybWFsaXplIHRoZSBjb3VudCBkYXRhIHRvIGhhdmUgZXZlbiBzYW1wbGluZyBhY3Jvc3Mgc2FtcGxlcwood2l0aCByZXNwZWN0IHRvIGxpYnJhcnkgc2l6ZSkgYW5kIGFwcHJveGltYXRlbHkgZXZlbiB2YXJpYW5jZToKCmBgYHtyfQp2c2QgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRkc19yYXcsIGJsaW5kID0gVFJVRSkKYGBgCgpOZXh0LCB3ZSBydW4gdGhlIFBDQSBhbmQgcmV0cmlldmUgdGhlIGRhdGEgdG8gcGxvdCB3aXRoICpnZ3Bsb3QyKjoKCmBgYHtyfQpwY2FEYXRhIDwtIHBsb3RQQ0EodnNkLAogICAgICAgICAgICAgICAgICAgbnRvcCA9IDUwMCwKICAgICAgICAgICAgICAgICAgIGludGdyb3VwID0gYygiQU1GIiwgIlRyZWF0bWVudCIpLAogICAgICAgICAgICAgICAgICAgcmV0dXJuRGF0YSA9IFRSVUUpCmBgYAoKV2UgZXh0cmFjdCB0aGUgcGVyY2VudGFnZSBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgZGlmZmVyZW50IHByaW5jaXBhbCBjb21wb25lbnRzLApzbyB3ZSBjYW4gbGF0ZXIgYWRkIHRoaXMgaW5mb3JtYXRpb24gdG8gdGhlIHBsb3Q6CgpgYGB7cn0KcGVyY2VudFZhciA8LSByb3VuZCgxMDAgKiBhdHRyKHBjYURhdGEsICJwZXJjZW50VmFyIikpCnBlcmNlbnRWYXIKYGBgCgpXZSBjcmVhdGUgYSBwbG90IHRpdGxlIHdpdGggdGhlIHNwZWNpZXMgbmFtZSBpbiBpdGFsaWMgdXNpbmcgdGhlCnNvbWV3aGF0IGJpemFycmUgYGV4cHJlc3Npb24oKWAgZnVuY3Rpb246IAoKYGBge3J9CnBsb3RfdGl0bGUgPC0gZXhwcmVzc2lvbigiUENBIG9mICIgKiBpdGFsaWMoR2x5Y2luZSB+IG1heCkgKiAiIHRyYW5zY3JpcHQgY291bnQgZGF0YSIpCmBgYAoKIyMjIFBsb3QgdGhlIFBDQSByZXN1bHRzCgpgYGB7cn0KZ2dwbG90KHBjYURhdGEsCiAgICAgICBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBBTUYsIHNoYXBlID0gVHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDYpICsKICB4bGFiKHBhc3RlMCgiUEMxOiAiLCBwZXJjZW50VmFyWzFdLCAiJSBvZiB2YXJpYW5jZSIpKSArCiAgeWxhYihwYXN0ZTAoIlBDMjogIiwgcGVyY2VudFZhclsyXSwgIiUgb2YgdmFyaWFuY2UiKSkgKwogIGdndGl0bGUocGxvdF90aXRsZSkKYGBgCgojIyMgUGxvdCBhZ2FpbiAtLSB3aXRoIHNhbXBsZSBuYW1lcwoKYGBge3J9CmxpYnJhcnkoZ2dyZXBlbCkKCnBjYV9wbG90MiA8LSBnZ3Bsb3QocGNhRGF0YSwKICAgICAgICAgICAgICBhZXMoUEMxLCBQQzIsIGNvbG9yID0gQU1GLCBzaGFwZSA9IFRyZWF0bWVudCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBuYW1lKSkgKwogIHhsYWIocGFzdGUwKCJQQzE6ICIsIHBlcmNlbnRWYXJbMV0sICIlIG9mIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLCBwZXJjZW50VmFyWzJdLCAiJSBvZiB2YXJpYW5jZSIpKSArCiAgZ2d0aXRsZShwbG90X3RpdGxlKQoKcHJpbnQocGNhX3Bsb3QyKQpgYGAKCjxicj4KCi0tLS0KCiMjIERFIGFuYWx5c2lzIC0tIGZ1bGwgZGF0YXNldAoKVGhlIGRlc2lnbiBoYXMgdHdvIGZhY3RvcnM6IGBBTUZgIGFuZCBgVHJlYXRtZW50YC4KUmF0aGVyIHRoYW4gZml0IGEgbXVsdGl2YXJpYXRlIG1vZGVsLCB3ZSBjYW4gc3RhcnQgYnkgbWVyZ2luZyB0aGUgdHdvIGludG8gYQpzaW5nbGUgZmFjdG9yIGNhbGxlZCBgZ3JvdXBgLCBhbmQgZml0IGEgdW5pdmFyaWF0ZSBtb2RlbCB3aXRoIHRoaXMgZmFjdG9yLgoKYGBge3J9CmRkc19yYXckZ3JvdXAgPC0gZmFjdG9yKHBhc3RlKGRkc19yYXckQU1GLCBkZHNfcmF3JFRyZWF0bWVudCwgc2VwID0gIl8iKSkKdGFibGUoZGRzX3JhdyRncm91cCkKYGBgCgpXZSB3aWxsIHNldCB0aGUgInJlZmVyZW5jZSIgbGV2ZWwgb2YgdGhlIGZhY3RvciB0byBiZSB0aGUgZG91YmxlIG5lZ2F0aXZlIGNvbnRyb2wKKGVtcHR5IHN1YnN0cmF0ZSwgbm8gQWdyb2JhY3RlcmlhKToKCmBgYHtyfQpkZHNfcmF3JGdyb3VwIDwtIHJlbGV2ZWwoZGRzX3JhdyRncm91cCwgcmVmID0gImNvbnRyb2xfbW9jayIpCmRkc19yYXckZ3JvdXAKYGBgCgpOZXh0LCB3ZSBzZXQgdGhlIGFuYWx5c2lzIGRlc2lnbjoKCmBgYHtyfQpkZXNpZ24oZGRzX3JhdykgPC0gZm9ybXVsYSh+IGdyb3VwKQpgYGAKCkFuZCBmaW5hbGx5LCB3ZSBwZXJmb3JtIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB3aXRoIHRoZSBgREVzZXEoKWAKZnVuY3Rpb246CgpgYGB7cn0KZGRzIDwtIERFU2VxKGRkc19yYXcpCmBgYAoKVGhlIGBERVNlcSgpYCBmdW5jdGlvbiBhYm92ZSBwZXJmb3JtcyB0aHJlZSBzdGVwcyBjb25zZWN1dGl2ZWx5OgogIAogIC0gYGVzdGltYXRlU2l6ZUZhY3RvcnMoKWAgLS0gIk5vcm1hbGl6YXRpb24iIGJ5IGxpYnJhcnkgc2l6ZS4KICAgIE5vdGUgdGhhdCAqREVTZXEyKiBkb2VzbuKAmXQgYWN0dWFsbHkgbm9ybWFsaXplIHRoZSBjb3VudHMsCiAgICBpdCB1c2VzIHJhdyBjb3VudHMgYW5kIGluY2x1ZGVzIHRoZSBzaXplIGZhY3RvcnMgaW4gdGhlIG1vZGVsaW5nLiAKICAKICAtIGBlc3RpbWF0ZURpc3BlcnNpb25zKClgIC0tIEVzdGltYXRlIGdlbmUtd2lzZSBkaXNwZXJzaW9ucy4KICAKICAtIGBuYmlub21XYWxkVGVzdChkZHNPYmopYCAtLSBGaXQgdGhlIG5lZ2F0aXZlIEJpbm9taWFsIEdMTSBhbmQgY2FsY3VsYXRlCiAgICBXYWxkIHN0YXRpc3RpY3MuCgpUaGVzZSBmdW5jdGlvbnMgY291bGQgYWxzbyBiZSBjYWxsZWQgc2VwYXJhdGVseSwKd2hpY2ggd291bGQgYmUgdXNlZnVsIGlmIHlvdSB3YW50IHRvIGJlIGFibGUgdG8gY2hhbmdlIG1vcmUgZGVmYXVsdHMuCgojIyMgVGhlIHJlc3VsdHMgdGFibGUKCmBgYHtyfQpyZXMgPC0gcmVzdWx0cyhkZHMpCnJlcwpgYGAKCkJ5IGRlZmF1bHQsIHRoZSByZXN1bHRzIHRhYmxlIHByaW50cyBzdGF0aXN0aWNzCioqY29tcGFyaW5nIHRoZSBsYXN0IGxldmVsIG9mIHRoZSBmYWN0b3Igd2l0aCB0aGUgZmlyc3QgbGV2ZWwqKjoKdGhhdCBpcywgbG9nLWZvbGQgY2hhbmdlIGFuZCBwLXZhbHVlcyBkZXNjcmliZQpkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZXNlIHR3byBsZXZlbHMgc3BlY2lmaWNhbGx5LgpIb3dldmVyLCB3ZSBjYW4gZWFzaWx5IGV4dHJhY3QgZXF1aXZhbGVudCBzdGF0aXN0aWNzIGZvciBhbnkgcGFpcndpc2UgY29tcGFyaXNvbgphbW9uZyBvdXIgZmFjdG9yIGxldmVscywgd2hpY2ggd2Ugd2lsbCBzZWUgbGF0ZXIuCgpGb3Igbm93LCB3ZSB3aWxsIGV4cGxvcmUgd2hhdCBlYWNoIGNvbHVtbiBpbiB0aGlzIHRhYmxlIG1lYW5zOgoKLSBUaGUgYGJhc2VNZWFuYCBjb2x1bW4gY29udGFpbnMgdGhlIG1lYW4gZXhwcmVzc2lvbiBsZXZlbCBhY3Jvc3MgYWxsIHNhbXBsZXMuCgotIFRoZSBgbG9nMkZvbGRDaGFuZ2VgIGNvbHVtbiBjb250YWlucyB0aGUgbG9nMi1mb2xkIGNoYW5nZSBvZiBnZW5lIGNvdW50cwogIGJldHdlZW4gdGhlIGNvbXBhcmVkIGxldmVscywgdGhhdCBpcywgaXQgcmVwcmVzZW50cyB0aGUgKmVmZmVjdCBzaXplKi4KICBBIGxvZzItZm9sZCBjaGFuZ2Ugb2YgMSBpbmRpY2F0ZXMgdGhhdCB0aGUgZXhwcmVzc2lvbiBpbiB0aGUgcmVmZXJlbmNlIGxldmVsCiAgaXMgMiB0aW1lcyBsb3dlciB0aGFuIHRoYXQgb2YgdGhlIG90aGVyIGxldmVsLCBhIGxvZzItZm9sZCBjaGFuZ2Ugb2YgMiBpbmRpY2F0ZXMKICBhIGZvdXItZm9sZCBkaWZmZXJlbmNlLCBhIGxvZzItZm9sZCBjaGFuZ2Ugb2YgMyBpbmRpY2F0ZXMgYW4gZWlnaHQtZm9sZCBkaWZmZXJlbmNlLAogIGFuZCBzbyBvbi4gU2ltaWxhcmx5LCBuZWdhdGl2ZSBsb2cyLWZvbGQgY2hhbmdlIHZhbHVlcyBpbmRpY2F0ZSBhbiBjaGFuZ2UKICBpbiBnZW5lIGNvdW50cyBpbiB0aGUgb3RoZXIgZGlyZWN0aW9uLgogIAotIFRoZSBgbGZjU0VgIGNvbHVtbiBpbmRpY2F0ZXMgdGhlIHVuY2VydGFpbnR5IGluIHRlcm1zIG9mIHRoZSBzdGFuZGFyZCBlcnJvcgogIChTRSkgb2YgdGhlIGxvZzItZm9sZCBjaGFuZ2UgZXN0aW1hdGUuCiAgCi0gVGhlIGBzdGF0YCBjb2x1bW4gaW5kaWNhdGVzIHRoZSB2YWx1ZSBmb3IgdGhlIFdhbGQgdGVzdCdzIHRlc3Qgc3RhdGlzdGljLgoKLSBUaGUgYHB2YWx1ZWAgY29sdW1uIHJlcG9ydGVkIHRoZSAqdW5jb3JyZWN0ZWQqIHAtdmFsdWUgZnJvbSB0aGUgV2FsZCB0ZXN0LgoKLSBCZWNhdXNlIHdlIGFyZSB0ZXN0aW5nIHNpZ25pZmljYW5jZSBmb3IgKm1hbnkqIGdlbmVzLAogIHdlIG5lZWQgdG8gY29ycmVjdCBmb3IgbXVsdGlwbGUgdGVzdGluZy4KICBERVNlcTIgdXNlcyB0aGUgQmVuamFtaW5pLUhvY2hiZXJnIEZhbHNlIERpc2NvdmVyeSBSYXRlIChGRFIpIGNvcnJlY3Rpb24sCiAgYW5kIHRoZXNlIHZhbHVlcyBhcmUgcmVwb3J0ZWQgaW4gdGhlIGNvbHVtbiBgcGFkamAgKGkuZS4sIGFkanVzdGVkIHAtdmFsdWUpLgoKQSBzdW1tYXJ5IG9mIHRoaXMgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBjb2x1bW4gY2FuIGJlIHNlZW4gYnkgcnVubmluZyB0aGUKYG1jb2xzKClgIGZ1bmN0aW9uOgoKYGBge3J9Cm1jb2xzKHJlcykKYGBgCgojIyMgYE5BYCB2YWx1ZXMgaW4gdGhlIHJlc3VsdHMgdGFibGUKClNvbWUgdmFsdWVzIGluIHRoZSByZXN1bHRzIHRhYmxlIGNhbiBiZSBzZXQgdG8gYE5BYCBmb3Igb25lIG9mIHRoZSBmb2xsb3dpbmcgcmVhc29uczoKCi0gSWYgd2l0aGluIGEgcm93LCBhbGwgc2FtcGxlcyBoYXZlIHplcm8gY291bnRzLAogIHRoZSBgYmFzZU1lYW5gIGNvbHVtbiB3aWxsIGJlIHplcm8sIGFuZCB0aGUgbG9nMi1mb2xkIGNoYW5nZSBlc3RpbWF0ZXMsCiAgcC12YWx1ZSBhbmQgYWRqdXN0ZWQgcC12YWx1ZSB3aWxsIGFsbCBiZSBzZXQgdG8gYE5BYC4KCi0gREVTZXEyIHBlcmZvcm1zIG91dGxpZXIgZGV0ZWN0aW9uIHVzaW5nIENvb2sncyBkaXN0YW5jZS4KICBJZiBhIGdlbmUgY29udGFpbnMgYSBzYW1wbGUgd2l0aCBhbiBjb3VudCBvdXRsaWVyLAogIGJvdGggdGhlIHAtdmFsdWUgYW5kIGFkanVzdGVkIHAtdmFsdWUgd2lsbCBiZSBzZXQgdG8gYE5BYC4KCi0gREVTZXEyIGFsc28gYXV0b21hdGljYWxseSBmaWx0ZXJzIGxvdyBtZWFuIGNvdW50IGdlbmVzIGluIHRoZSBzZW5zZSB0aGF0IGl0CiAgZG9lcyBub3QgaW5jbHVkZSB0aGVtIGluIHRoZSBtdWx0aXBsZSB0ZXN0aW5nIGNvcnJlY3Rpb24uCiAgVGhlcmVmb3JlLCBpbiBzdWNoIGNhc2VzLCB0aGUgcC12YWx1ZSB3aWxsIG5vdCBiZSBgTkFgLCBidXQgdGhlIGFkanVzdGVkIHAtdmFsdWUKICB3aWxsIGJlLgogIAogIEJlY2F1c2Ugd2UgaGF2ZSB2ZXJ5IGxvdyBwb3dlciB0byBkZXRlY3QgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gZm9yIHN1Y2gKICBsb3ctY291bnQgZ2VuZXMsIGl0IGlzIGJlbmVmaWNpYWwgdG8gcmVtb3ZlIHRoZW0gcHJpb3IgdG8gdGhlIG11bHRpcGxlIHRlc3RpbmcKICBjb3JyZWN0aW9uLCBzbyB0aGlzIGNvcnJlY3Rpb24gYmVjb21lcyBsZXNzIHNldmVyZSBmb3IgdGhlIHJlbWFpbmluZyBnZW5lcy4KCkxldCdzIHNlZSBob3cgbWFueSBnZW5lcyBoYXZlIGBOQWAgcC12YWx1ZXM6CgpgYGB7cn0KIyBOdW1iZXIgb2YgZ2VuZXMgd2l0aCBOQSBwLXZhbHVlOgpzdW0oaXMubmEocmVzJHB2YWx1ZSkpCgojIEFzIGEgcHJvcG9ydGlvbiBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSB0ZXN0OgpzdW0oaXMubmEocmVzJHB2YWx1ZSkpIC8gbnJvdyhyZXMpCmBgYAoKQW5kIGBOQWAgYWRqdXN0ZWQgcC12YWx1ZXM6CgpgYGB7cn0KIyBOdW1iZXIgb2YgZ2VuZXMgd2l0aCBOQSBwLXZhbHVlOgpzdW0oaXMubmEocmVzJHBhZGopKQoKIyBBcyBhIHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgdGVzdDoKc3VtKGlzLm5hKHJlcyRwYWRqKSkgLyBucm93KHJlcykKYGBgCgo8YnI+CgotLS0tCgojIyBERSBhbmFseXNpcyAtLSBjb250cmFzdCB0d28gY3VzdG9tIGdyb3VwcwoKQWx0aG91Z2ggbm90IGRpc3BsYXllZCBpbiBhIHBhcnRpY3VsYXJseSByZWFkYWJsZSB3YXlzLAp3ZSBjYW4gc2VlIHdoaWNoIHBhaXJ3aXNlIGNvbnRyYXN0cyBiZXR3ZWVuIGRpZmZlcmVudCBsZXZlbHMgb2YgdGhlIGZhY3RvcgphcmUgYXZhaWxhYmxlOgoKYGBge3J9CnJlc3VsdHNOYW1lcyhkZHMpCmBgYAoKTm90IGFsbCBwYWlyd2lzZSBjb250cmFzdHMgYmV0d2VlbiB0aGUgNSBsZXZlbHMgaW4gb3VyIGBncm91cGAgZmFjdG9yIGFyZSBhdmFpbGFibGU6Cmluc3RlYWQsIGBjb250cm9sX21vY2tgLCB3aGljaCB3ZSBzZXQgYXMgdGhlIHJlZmVyZW5jZSBsZXZlbCwgaXMgYmVpbmcgY29tcGFyZWQKd2l0aCBhbGwgNCBvdGhlciBsZXZlbHMuCgpBYm92ZSwgd2UgbG9va2VkIGF0IHRoZSByZXN1bHRzIChwLXZhbHVlcyBhbmQgc28gb24pIGZvciB0aGUgbGFzdCBvZiB0aGVzZQpjb21wYXJpc29ucywgd2hpY2ggaXMgd2hhdCBERVNlcTIgd2lsbCBzaG93IGJ5IGRlZmF1bHQuClRvIHNlZSB0aGUgcmVzdWx0cyB0YWJsZSBmb3Igb25lIG9mIHRoZSBvdGhlciAzIGNvbXBhcmlzb25zLAp3ZSBwYXNzIGEgdmVjdG9yIHRvIHRoZSBgY29udHJhc3RgIGFyZ3VtZW50IG9mIHRoZSBgcmVzdWx0cygpYCBmdW5jdGlvbiB3aXRoCnRoZSBmYWN0b3IgKGBncm91cGApIGFuZCB0aGUgdHdvIGxldmVscyB0byBiZSBjb250cmFzdGVkOgoKYGBge3J9Cm15X2NvbnRyYXN0IDwtIGMoIlJpX21vY2siLCAiY29udHJvbF9tb2NrIikKCnJlcyA8LSByZXN1bHRzKGRkcywKICAgICAgICAgICAgICAgY29udHJhc3QgPSBjKCJncm91cCIsIG15X2NvbnRyYXN0KSkKYGBgCgpIb3cgbWFueSBhZGp1c3RlZCBwLXZhbHVlcyB3ZXJlIGxlc3MgdGhhbiAwLjE/CgpgYGB7cn0Kc3VtKHJlcyRwYWRqIDwgMC4xLCBuYS5ybSA9IFRSVUUpCmBgYAoKV2UnbGwgYWxzbyBjcmVhdGUgYW4gb2JqZWN0IHdpdGggYWRqdXN0ZWQgKHNocnVua2VuKSBMRkMgZXN0aW1hdGVzIHdoaWNoIHdpbGwKYmUgdXNlZnVsIGZvciB2aXN1YWxpemF0aW9uIGFuZCByYW5raW5nIG9mIGdlbmVzIGJ5IExGQy4KCmBgYHtyfQpteV9jb2VmIDwtIHBhc3RlMCgiZ3JvdXBfIiwgcGFzdGUwKG15X2NvbnRyYXN0LCBjb2xsYXBzZSA9ICJfdnNfIikpCm15X2NvZWYKcmVzX0xGQyA8LSBsZmNTaHJpbmsoZGRzLCBjb2VmID0gbXlfY29lZiwgdHlwZSA9ICJhcGVnbG0iLCBsZmNUaHJlc2hvbGQgPSAxKQpgYGAKCk5vdGUgdGhhdCBoZXJlLCB3ZSBoYWQgdG8gcHJvdmlkZSB0aGUgY29udHJhc3QgKCJjb2VmZmljaWVudCIpIGluIHRoZSBmb3JtYXQKZ2l2ZW4gYnkgYHJlc3VsdHNOYW1lcyhkZHMpYCwgd2hpY2ggbG9va3MgY29uZnVzaW5nIGJlY2F1c2UgaXQgdXNlcyB1bmRlcnNjb3JlcwphcyBhIHNlcGFyYXRvciwgYW5kIG91ciBmYWN0b3IgbGV2ZWxzIGFsc28gY29udGFpbiB1bmRlcnNjb3Jlcy4KCjxicj4KCi0tLS0KCiMjIEV4cGxvcmluZyB0aGUgcmVzdWx0cwoKIyMjIE1BLXBsb3Q6CgpGb3IgYSBuaWNlIG92ZXJ2aWV3IG9mIHRoZSByZXN1bHRzLCB3ZSBjYW4gcGxvdCBhIHNvLWNhbGxlZCAiTUEgcGxvdCIsCndoaWNoIHBsb3RzLCBmb3IgZWFjaCBnZW5lLApjb3VudCBkaWZmZXJlbmNlcyAoYXMgTEZDKSBiZXR3ZWVuIHR3byBncm91cHMgb24gdGhlIHktYXhpcywKYW5kIG1lYW4gY291bnRzIGFjcm9zcyBib3RoIGdyb3VwcyBvbiB0aGUgeC1heGlzLgoKV2UgY2FuIGNyZWF0ZSBhbiBNQSBwbG90IHVzaW5nIERFU2VxMidzIGBwbG90TUFgIGZ1bmN0aW9uLAp3aXRoIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzICh1c2luZyBhbHBoYSA9IDAuMSkgaW4gYmx1ZToKCmBgYHtyfQpwbG90TUEocmVzLCB5bGltID0gYygtNSwgNSkpCmBgYAoKVG8gYmUgYWJsZSB0byBjdXN0b21pemUgdGhlIHBsb3QsCndlJ2xsIHVzZSBgcmV0dXJuRGF0YSA9IFRSVUVgIGxpa2Ugd2UgaGF2ZSBkb25lIHdpdGggcHJldmlvdXMgcGxvdHMsCmFuZCB0aGVuIHBsb3QgdGhlIHJlc3VsdGluZyBkYXRhZnJhbWUgd2l0aCAqZ2dwbG90Mio6CgpgYGB7cn0KZCA8LSBwbG90TUEocmVzLCByZXR1cm5EYXRhID0gVFJVRSkKCmdncGxvdChkLCBhZXMoeCA9IG1lYW4sIHkgPSBsZmMsIGNvbG9yID0gaXNERSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xMCwgMTApKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2dyZXk1MCcsICdibHVlJykpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJNZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIiwKICAgICAgIHkgPSAiTEZDIikKYGBgCgpXZSBjYW4gc2VlIHRoYXQgbG93bHktZXhwcmVzc2VkIGdlbmVzIHRlbmQgdG8gZGV2aWF0ZSBmcm9tIGFuIExGQyBvZiAwCihzYW1lIG1lYW4gZXhwcmVzc2lvbiBsZXZlbHMgaW4gdGhlIHR3byBncm91cHMpIG11Y2ggbW9yZSB0aGFuIDAuCkhvd2V2ZXIsIHRoaXMgaXMgYW4gYXJ0aWZhY3Qgb2Ygbm9pc2Ugb3ZlcnBvd2VyaW5nIHNpZ25hbCB3aXRoIHN1Y2ggbG93CmV4cHJlc3Npb24gdmFsdWVzLiBXZSBjYW4gYWxzbyBzZWUgdGhhdCBnZW5lcyBhcmUgbmV2ZXIgc2lnbmlmaWNhbnRseSBleHByZXNzZWQKaW4gdGhlIHNhbWUgZmFyIGxlZnQgcGFydCBvZiB0aGUgcGxvdCBkdWUgdG8gdGhpcyBsYWNrIG9mIHBvd2VyLgoKREVTZXEyIHByb3ZpZGVzIHNldmVyYWwgbWV0aG9kcyB0byBhZGp1c3QgTEZDIGVzdGltYXRlcyBmb3IgdGhlIGxvdy1leHByZXNzaW9uCmJpYXMsIG9uZSBvZiB3aGljaCB3ZSB1c2VkIGFib3ZlIHRvIHByb2R1Y2UgdGhlIGByZXNfTEZDYCBvYmplY3QuCkxldCdzIGNyZWF0ZSBhbm90aGVyIE1BIHBsb3QgZm9yIHRoZXNlIGFkanVzdGVkIGVzdGltYXRlczoKCmBgYHtyfQpkIDwtIHBsb3RNQShyZXNfTEZDLCB5bGltID0gYygtNSwgNSksIHJldHVybkRhdGEgPSBUUlVFKQoKZ2dwbG90KGQsIGFlcyh4ID0gbWVhbiwgeSA9IGxmYywgY29sb3IgPSBpc0RFKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2dyZXk1MCcsICdibHVlJykpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJNZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIiwKICAgICAgIHkgPSAiU2hydW5rZW4gTEZDIikKYGBgCgpXZSBjYW4gYWxzbyBtYWtlIHRoaXMgcGxvdCBpbnRlcmFjdGl2ZSB3aXRoIFBsb3RseSwgc28gd2UgY2FuIHNlZSB0aGUgaWRlbnRpdHkKb2YgZWFjaCBnZW5lIGJ5IGhvdmVyaW5nIG92ZXIgdGhlIHBvaW50OgoKYGBge3J9CmxpYnJhcnkocGxvdGx5KQoKZCRnZW5lIDwtIHJvd25hbWVzKGQpCgpwX21hIDwtIGdncGxvdChkLAogICAgICAgICAgICAgICBhZXMoeCA9IG1lYW4sIHkgPSBsZmMsIGNvbG9yID0gaXNERSwgdGV4dCA9IGdlbmUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnZ3JleTUwJywgJ2JsdWUnKSkgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgbGFicyh4ID0gIk1lYW4gb2Ygbm9ybWFsaXplZCBjb3VudHMiLAogICAgICAgeSA9ICJTaHJ1bmtlbiBMRkMiKQoKZ2dwbG90bHkocF9tYSwgdG9vbHRpcCA9ICJ0ZXh0IikKYGBgCgojIyMgUGxvdCBzcGVjaWZpYyBnZW5lcwoKV2UgY2FuIGFsc28gY3JlYXRlIHBsb3Qgb2YgZXhwcmVzc2lvbiBsZXZlbHMgaW4gaW5kaXZpZHVhbCBnZW5lcywKd2hpY2ggd2lsbCBiZSBlc3BlY2lhbGx5IGludGVyZXN0aW5nIGZvciBnZW5lcyB3aXRoIGhpZ2hseSBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwKZXhwcmVzc2lvbi4KCkxldCdzIHBsb3QgdGhlIHRvcC01IG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXM6CgpgYGB7ciwgZXZhbD1UUlVFfQp0b3A1IDwtIHJvdy5uYW1lcyhyZXNbb3JkZXIocmVzJHBhZGopLCBdWzE6NSwgXSkKCmZvcihmZ2VuZSBpbiB0b3A1KSB7CiAgCiAgZCA8LSBwbG90Q291bnRzKGRkcywKICAgICAgICAgICAgICAgICAgZ2VuZSA9IGZnZW5lLAogICAgICAgICAgICAgICAgICBpbnRncm91cCA9ICJncm91cCIsCiAgICAgICAgICAgICAgICAgIHJldHVybkRhdGEgPSBUUlVFKQoKICBwIDwtIGdncGxvdChkLCBhZXMoeCA9IGdyb3VwLCB5ID0gY291bnQpKSArCiAgICAgICAgICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHcgPSAwLjEsIGggPSAwKSkgKwogICAgICAgICAgbGFicyh0aXRsZSA9IGZnZW5lKSArCiAgICAgICAgICB0aGVtZV9idygpCiAgCiAgcHJpbnQocCkKfQoKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KIyAiU29seWMwMmcwNjk0MzAuNCIgIlNvbHljMDZnMDUwNTkwLjMiICJTb2x5YzAxZzExMTIyMC4zIiAiU29seWMxMGcwNDcwMzAuMyIgIlNvbHljMDRnMDc5MzMwLjIiCmQgPC0gcGxvdENvdW50cyhkZHMsCiAgICAgICAgICAgICAgICBnZW5lID0gIlNvbHljMDJnMDY5NDMwLjQiLAogICAgICAgICAgICAgICAgaW50Z3JvdXAgPSAiZ3JvdXAiLAogICAgICAgICAgICAgICAgcmV0dXJuRGF0YSA9IFRSVUUpCgpnZ3Bsb3QoZCwgYWVzKHggPSBncm91cCwgeSA9IGNvdW50KSkgKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIodyA9IDAuMSwgaCA9IDApKSArCiAgbGFicyh0aXRsZSA9IGZnZW5lKSArCiAgdGhlbWVfYncoKQpgYGAKCiMjIyBIZWF0bWFwCgpXZSBjYW4gY3JlYXRlIGhlYXRtYXBzIHdpdGggdGhlIGBwaGVhdG1hcGAgZnVuY3Rpb24gLS0KZm9yIGluc3RhbmNlLCBmb3IgdGhlIDIwIG1vc3QgaGlnaGx5IGV4cHJlc3NlZCBnZW5lczoKCmBgYHtyLCBldmFsPVRSVUV9Cm50ZCA8LSBub3JtVHJhbnNmb3JtKGRkcykKc2VsZWN0aW9uIDwtIG9yZGVyKHJvd01lYW5zKGNvdW50cyhkZHMsIG5vcm1hbGl6ZWQgPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gVFJVRSlbMToyMF0KbnRkX3NlbCA8LSBhc3NheShudGQpW3NlbGVjdGlvbiwgXQpkZl9tZXRhIDwtIGFzLmRhdGEuZnJhbWUoY29sRGF0YShkZHMpWywgYygiQU1GIiwgIlRyZWF0bWVudCIpXSkKYGBgCgpgYGB7ciwgZXZhbD1UUlVFfQpwaGVhdG1hcChudGRfc2VsLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkZl9tZXRhKQpgYGAKCiMjIyBFeHBvcnQgdGhlIHJlc3VsdHMKClRCQQoKPGJyPgoKLS0tLQoKIyMgREUgYW5hbHlzaXMgLS0gd2l0aCB0d28gZmFjdG9ycwoKVEJBCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KZGRzXzJmX3JhdyA8LSBkZHNfcmF3CgojIEFNRiArIFRyZWF0bWVudDogVGVzdCBmb3IgdGhlIGVmZmVjdCBvZiBUcmVhdG1lbnQgKHRoZSBsYXN0IGZhY3RvciksCiMgLi4uY29udHJvbGxpbmcgZm9yIHRoZSBlZmZlY3Qgb2YgQU1GICh0aGUgZmlyc3QgZmFjdG9yKS4KZGVzaWduKGRkc18yZl9yYXcpIDwtIGZvcm11bGEofiBBTUYgKyBUcmVhdG1lbnQpCgojIE9yIHVzZSBpbnRlcmFjdGlvbiB0ZXJtOiDiiLwgQU1GICsgVHJlYXRtZW50ICsgQU1GOlRyZWF0bWVudApgYGAKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQojIFJ1biBERVNlcSB3aXRoIHRoZSBuZXcgZGVzaWduOgpkZHNfMmYgPC0gREVTZXEoZGRzXzJmX3JhdykKYGBgCgo=